package com.esdk.utils;

import com.esdk.esdk;

import java.io.Closeable;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class RedisLock implements Closeable,AutoCloseable,Lock{
  private static long intervalWaitTime=500;
  private int _waitSec=30;
  private boolean stopFlag=false;
  private boolean isLockOwner=false;
  private int _expiredSec=60;
  private String _lockKey;

  public RedisLock(String lockKey){
    this._lockKey=lockKey;
    this._expiredSec=60;
    this._waitSec=30;
  }


  public RedisLock(String lockKey,int expiredSec,int waitSec) throws InterruptedException{
    this._lockKey=lockKey;
    this._expiredSec=expiredSec;
    lock(waitSec);
  }

  /**不建议使用*/
  @Override
  public void lock(){
    try{
      lockInterruptibly();
    }catch(InterruptedException e){
      esdk.warn(e);
    }
  }

  /**不建议使用*/
  @Override
  public void lockInterruptibly() throws InterruptedException{
    while(!tryLock()){
      Thread.sleep(intervalWaitTime);
    }
  }

  /**不建议使用*/
  @Override
  public boolean tryLock(){
    boolean locked=RedisUtils.setnx(_lockKey,LocalDateTime.now(),_expiredSec);
      return locked;
  }

  /**不建议使用*/
  @Override
  public boolean tryLock(long sec,TimeUnit unit) throws InterruptedException{
    return lock((int)unit.toSeconds(sec));
  }

  /**建议使用*/
  public boolean lock(int waitSec) throws InterruptedException{
    this._waitSec=waitSec;//TODO确定一下是否正确
    long start=System.currentTimeMillis();
    boolean locked=tryLock();
    do{
      if(locked){
        stopFlag=true;
        isLockOwner=true;
//        esdk.sout(LocalDateTime.now()+": "+Thread.currentThread().getName()+"已获取锁");
        break;
      } else{
        try{
          synchronized(this) {
            this.wait(intervalWaitTime);
          }
        }catch(InterruptedException e){
          throw new RuntimeException(e);
        }
        long current=System.currentTimeMillis();
        if((current-start)>_waitSec*1000){
          throw new InterruptedException("获取分布锁["+_lockKey+"]超时"+_waitSec+"秒");
        }
      }
    }while(!stopFlag);
    return locked;
  }

  @Override
  public void unlock(){
    stopFlag=true;
    if(isLockOwner){
      RedisUtils.del(_lockKey);
//      esdk.sout(LocalDateTime.now()+": "+Thread.currentThread().getName()+"已释放锁");
    }
  }

  @Override
  public Condition newCondition(){
    throw new UnsupportedOperationException();
  }

  @Override
  public void close() throws IOException{
    unlock();
  }

  public static RedisLock tryLock(String lockKey,int expiredSec,int waitSec) throws InterruptedException{
    return new RedisLock(lockKey,expiredSec,waitSec);
  }

  public static void main(String[] args){
    try(RedisLock redisLock=tryLock("com.baijian.cas.schedule.TestCron",60,5)){
      esdk.tool.sleep(1000);
      esdk.sout("do something");
    }catch(Exception e){
      throw new RuntimeException(e);
    }

    try(RedisLock locker=new RedisLock("com.baijian.cas.schedule.TestCron")){
      locker.lock(5);
      esdk.sout("do anything");
    }catch(IOException e){
      throw new RuntimeException(e);
    }catch(InterruptedException e){
      throw new RuntimeException(e);
    }
  }

}
